#include "cJSON.h"              // JSON解析库
#include "netmgr.h"             // 网络管理模块
#include "tal_api.h"             // Tuya抽象层API
#include "tkl_output.h"          // Tuya底层输出接口
#include "tkl_gpio.h"           // gpio模块
#include "tuya_config.h"         // Tuya配置头文件
#include "tuya_iot.h"            // Tuya IoT核心功能
#include "tuya_iot_dp.h"         // Tuya数据点处理
#include "tal_cli.h"             // 命令行接口
#include "tuya_authorize.h"      // 设备授权相关功能
#include <assert.h>               // 断言库
#include "tkl_pwm.h"            // Tuya工具层PWM相关API
/*
 * 根据条件编译导入不同的网络模块
 */
#if defined(ENABLE_WIFI) && (ENABLE_WIFI == 1)
#include "netconn_wifi.h"        // WiFi网络连接模块
#endif
#if defined(ENABLE_WIRED) && (ENABLE_WIRED == 1)
#include "netconn_wired.h"       // 有线网络连接模块
#endif
#if defined(ENABLE_CELLULAR) && (ENABLE_CELLULAR == 1)
#include "netconn_cellular.h"    // 蜂窝网络连接模块
#endif
#if defined(ENABLE_LIBLWIP) && (ENABLE_LIBLWIP == 1)
#include "lwip_init.h"           // LWIP初始化模块
#endif

#include "reset_netcfg.h"       // 网络配置重置模块

#if defined(ENABLE_QRCODE) && (ENABLE_QRCODE == 1)
#include "qrencode_print.h"    // QR码打印模块
#endif
// 如果未定义项目版本定义，则定义PROJECT_VERSION="1.0.0"
#ifndef PROJECT_VERSION
#define PROJECT_VERSION "1.0.0"
#endif

static THREAD_HANDLE sg_pwm_handle; // PWM线程句柄
#define TASK_PWM_PRIORITY        THREAD_PRIO_2 // PWM任务优先级
#define TASK_PWM_SIZE            4096 // PWM任务栈大小
#define TEST_DUTY_STEP           50  // 测试占空比步进值
#define TEST_DUTY_MAX            10000 // 测试占空比最大值
// 定义舵机频率、顺时针占空比和逆时针占空比（需要测试后才得知）
#define SERVO_PWM_FREQUENCY      50 // 舵机标准频率：50Hz (20ms周期)
#define SERVO_CLOCK_WIRE_DUTY    600 // 舵机顺时针缓慢转动占空比
#define SERVO_AN_CLOCK_WIRE_DUTY 800 // 舵机逆时针缓慢转动占空比

// pwm引脚定义
#define PWM_ID  TUYA_PWM_NUM_0  // P18

// 外部函数声明 命令行接口初始化函数声明
extern void tuya_app_cli_init(void);

/*
 * 全局变量定义
 */
// Tuya设备客户端对象，用于与Tuya IoT平台通信
tuya_iot_client_t client;

// Tuya设备授权信息（包含UUID和AuthKey）
tuya_iot_license_t license;


/**
 * 开关按键
 * 顺时针：90ms-30度， 100ms-50度，130ms-70度， 250-270ms-90度， 400ms-135度， 450ms-150度， 500ms-180度
 * 逆时针：70ms-10度， 80ms-20度，140ms-30度，170ms-40度，240ms-45度，400ms-60度，470ms-70度，660ms-90度
 */
static void servo_task_switch(bool is_switch_on)
{
    PR_NOTICE("Servo switch mode: %s", is_switch_on ? "ON" : "OFF");
    // PWM初始化配置 - 针对舵机控制
    OPERATE_RET rt = OPRT_OK;
    TUYA_PWM_BASE_CFG_T pwm_cfg = {
        .duty = 0, // 初始占空比为
        .frequency = SERVO_PWM_FREQUENCY, // 舵机标准频率：50Hz
        .polarity = TUYA_PWM_NEGATIVE, // PWM极性：负逻辑
    };
    // 初始化PWM通道，若失败则跳转到退出标签
    TUYA_CALL_ERR_GOTO(tkl_pwm_init(PWM_ID, &pwm_cfg), __EXIT);
    // 启动PWM控制舵机
    TUYA_CALL_ERR_GOTO(tkl_pwm_start(PWM_ID), __EXIT);
    if (is_switch_on) {
        // 逆时针按下70度后回弹40度
        TUYA_CALL_ERR_LOG(tkl_pwm_duty_set(PWM_ID, 800)); // 设置PWM占空比
        TUYA_CALL_ERR_LOG(tkl_pwm_start(PWM_ID));
        tal_system_sleep(470);
        TUYA_CALL_ERR_LOG(tkl_pwm_duty_set(PWM_ID, 0)); // 设置PWM占空比
        TUYA_CALL_ERR_LOG(tkl_pwm_start(PWM_ID));
        tal_system_sleep(1000); // 按下700ms
        // 回弹40度
        TUYA_CALL_ERR_LOG(tkl_pwm_duty_set(PWM_ID, 600)); // 设置PWM占空比
        TUYA_CALL_ERR_LOG(tkl_pwm_start(PWM_ID));
        tal_system_sleep(100);
        TUYA_CALL_ERR_LOG(tkl_pwm_duty_set(PWM_ID, 0)); // 设置PWM占空比
        TUYA_CALL_ERR_LOG(tkl_pwm_start(PWM_ID));


    } else {
        // 顺时针按下70度后回弹50度
        TUYA_CALL_ERR_LOG(tkl_pwm_duty_set(PWM_ID, 600)); // 设置PWM占空比
        TUYA_CALL_ERR_LOG(tkl_pwm_start(PWM_ID));
        tal_system_sleep(100);
        TUYA_CALL_ERR_LOG(tkl_pwm_duty_set(PWM_ID, 0)); // 设置PWM占空比
        TUYA_CALL_ERR_LOG(tkl_pwm_start(PWM_ID));
        tal_system_sleep(1000); // 按下700ms
        // 回弹50度
        TUYA_CALL_ERR_LOG(tkl_pwm_duty_set(PWM_ID, 800)); // 设置PWM占空比
        TUYA_CALL_ERR_LOG(tkl_pwm_start(PWM_ID));
        tal_system_sleep(140);
        TUYA_CALL_ERR_LOG(tkl_pwm_duty_set(PWM_ID, 0)); // 设置PWM占空比
        TUYA_CALL_ERR_LOG(tkl_pwm_start(PWM_ID));
    }


    __EXIT: // 退出标签
        PR_NOTICE("舵机按键回弹任务测试完毕，开始停止pwm输出");
        TUYA_CALL_ERR_LOG(tkl_pwm_stop(PWM_ID)); // 停止PWM输出
        tal_thread_delete(sg_pwm_handle); // 删除任务
        return;
}


// 用户自定义日志输出回调函数 在本示例中，它将使用UART0作为日志输出端口
void user_log_output_cb(const char *str)
{
    tkl_log_output(str); // 调用Tuya底层日志输出函数，无锁不阻塞
}

/**
 * 用户自定义固件升级通知回调函数
 * 当设备接收到OTA（空中下载）升级请求时，系统会调用此函数
 * @param client:客户端对象
 * @param upgrade:升级请求信息（JSON格式）
 * @return void
 */
void user_upgrade_notify_on(tuya_iot_client_t *client, cJSON *upgrade)
{
    PR_INFO("----- Upgrade information -----");
    PR_INFO("OTA Channel: %d", cJSON_GetObjectItem(upgrade, "type")->valueint);
    PR_INFO("Version: %s", cJSON_GetObjectItem(upgrade, "version")->valuestring);
    PR_INFO("Size: %s", cJSON_GetObjectItem(upgrade, "size")->valuestring);
    PR_INFO("MD5: %s", cJSON_GetObjectItem(upgrade, "md5")->valuestring);
    PR_INFO("HMAC: %s", cJSON_GetObjectItem(upgrade, "hmac")->valuestring);
    PR_INFO("URL: %s", cJSON_GetObjectItem(upgrade, "url")->valuestring);
    PR_INFO("HTTPS URL: %s", cJSON_GetObjectItem(upgrade, "httpsUrl")->valuestring);
}

/**
 * 用户自定义事件处理器
 * 处理从Tuya IoT平台接收到的各种事件
 * @param client:客户端对象
 * @param event:事件信息结构体
 * @return void
 */
void user_event_handler_on(tuya_iot_client_t *client, tuya_event_msg_t *event)
{
    // 打印事件ID和当前设备的可用堆内存
    PR_DEBUG("xxs Tuya事件ID :%d(%s)", event->id, EVENT_ID2STR(event->id));
    PR_INFO("xxs 设备可用堆内存: %d", tal_system_get_free_heap_size());
    // 根据事件ID处理不同类型的事件
    switch (event->id) {
    case TUYA_EVENT_BIND_START:
        PR_INFO("xxs设备开始绑定事件!");
        break;

    // 打印二维码用于Tuya APP绑定
    case TUYA_EVENT_DIRECT_MQTT_CONNECTED: {
#if defined(ENABLE_QRCODE) && (ENABLE_QRCODE == 1)
        char buffer[255];
        // 生成设备绑定二维码链接
        sprintf(buffer, "https://smartapp.tuya.com/s/p?p=%s&uuid=%s&v=2.0", TUYA_PRODUCT_ID, license.uuid);
        // 输出二维码到日志
        qrcode_string_output(buffer, user_log_output_cb, 0);
#endif
    } break;

    // 与Tuya云平台的MQTT连接已建立，设备上线 
    case TUYA_EVENT_MQTT_CONNECTED:
        PR_INFO("xxs 与Tuya云平台的MQTT连接已建立，设备上线, MQTT已连接!");
        break;

    // 收到固件升级请求
    case TUYA_EVENT_UPGRADE_NOTIFY:
        // 调用升级通知处理函数
        PR_INFO("xxs收到固件升级请求事件");
        user_upgrade_notify_on(client, event->value.asJSON);
        break;

    /* Sync time with tuya Cloud */
    case TUYA_EVENT_TIMESTAMP_SYNC:
        PR_INFO("xxs与Tuya云平台同步时间戳事件:%d", event->value.asInteger);
        // 设置设备系统时间为Tuya云平台时间
        tal_time_set_posix(event->value.asInteger, 1);
        break;
    case TUYA_EVENT_RESET:
        PR_INFO("xxs设备重置事件:%d", event->value.asInteger);
        break;

    // 收到对象类型的数据点(DP)
    case TUYA_EVENT_DP_RECEIVE_OBJ: {
        dp_obj_recv_t *dpobj = event->value.dpobj;
        PR_DEBUG("xxs接收到涂鸦云平台DP命令 命令cmd_tp为 :%d dtt_tp:%d 数据数量dpscnt为:%u", dpobj->cmd_tp, dpobj->dtt_tp, dpobj->dpscnt);
        if (dpobj->devid != NULL) {
            PR_DEBUG("xxs event->value.dpobj->devid设备ID为: %s", dpobj->devid);
        }

        uint32_t index = 0;
        PR_INFO("xxs 开始遍历所有接收到的DP数据点");
        for (index = 0; index < dpobj->dpscnt; index++) {
            dp_obj_t *dp = dpobj->dps + index;
            PR_DEBUG("xxs 数据点索引 idx:%d dpid:%d 类型type:%d 时间戳ts:%u", index, dp->id, dp->type, dp->time_stamp);
            // 根据数据点类型处理不同格式的数据
            switch (dp->type) {
            case PROP_BOOL: {
                PR_DEBUG("xxs dp数据点类型为布尔值:%d", dp->value.dp_bool);
                if (dp->value.dp_bool) {
                    PR_INFO("xxs:%d", dp->value.dp_bool);
                    servo_task_switch(false);
                } else {
                    servo_task_switch(true);
                }
                break;
            }
            case PROP_VALUE: {
                PR_DEBUG("xxs dp数据点类型为整数值:%d", dp->value.dp_value);
                break;
            }
            case PROP_STR: {
                PR_DEBUG("xxs dp数据点类型为字符串值:%s", dp->value.dp_str);
                break;
            }
            case PROP_ENUM: {
                PR_DEBUG("xxs dp数据点类型为枚举值:%u", dp->value.dp_enum);
                break;
            }
            case PROP_BITMAP: {
                PR_DEBUG("xxs dp数据点类型为位图值:0x%X", dp->value.dp_bitmap);
                break;
            }
            default: {
                PR_ERR("xxs dp数据点索引idx:%d dpid:%d 类型type:%d 时间戳ts:%u 是无效的", index, dp->id, dp->type, dp->time_stamp);
                break;
            }
            } 
        }
        // 将收到的数据点原样上报回云平台（实际应用中应该根据业务逻辑处理后再上报）
        tuya_iot_dp_obj_report(client, dpobj->devid, dpobj->dps, dpobj->dpscnt, 0);

    } break;

    // 收到原始类型的数据点(DP)
    case TUYA_EVENT_DP_RECEIVE_RAW: {
        dp_raw_recv_t *dpraw = event->value.dpraw;
        PR_DEBUG("xxs 接收到原始类型的数据点DP命令 cmd_tp为:%d dtt_tp为:%d", dpraw->cmd_tp, dpraw->dtt_tp);
        if (dpraw->devid != NULL) {
            PR_DEBUG("xxs event->value.dpraw->devid设备ID为:%s", dpraw->devid);
        }

        uint32_t index = 0;
        // 打印原始数据点内容
        dp_raw_t *dp = &dpraw->dp;
        PR_DEBUG("xxs 打印原始数据点内容dpid:%d 类型:RAW 长度:%d 数据:", dp->id, dp->len);
        for (index = 0; index < dp->len; index++) {
            PR_DEBUG_RAW("%02x", dp->data[index]);
        }
        // 将收到的原始数据点原样上报回云平台
        tuya_iot_dp_raw_report(client, dpraw->devid, &dpraw->dp, 3);

    } break;

    // 可在此处添加其他事件处理

    default:
        break;
    }
}

/**
 * 用户自定义网络检查回调函数
 * 系统会每隔1秒调用此函数检查网络连接状态
 * @return true:网络已连接
 * @return false:网络未连接
 */
bool user_network_check(void)
{
    netmgr_status_e status = NETMGR_LINK_DOWN;
    // 获取当前网络连接状态
    netmgr_conn_get(NETCONN_AUTO, NETCONN_CMD_STATUS, &status);
    // 如果网络状态不是断开状态，则返回true
    return status == NETMGR_LINK_DOWN ? false : true;
}

/**
 * 用户主函数
 * 应用程序的核心入口，负责初始化系统资源、配置Tuya IoT客户端和启动主循环
 * @return void
 */
void user_main(void)
{
    int rt = OPRT_OK; // 操作返回码，初始化为成功

    // 初始化JSON库的内存分配函数
    // 使用Tuya抽象层的内存分配函数
    // 使用Tuya抽象层的内存释放函数
    cJSON_InitHooks(&(cJSON_Hooks){.malloc_fn = tal_malloc, .free_fn = tal_free});
    // 初始化日志系统，设置日志级别为DEBUG，缓冲区大小为1024字节
    tal_log_init(TAL_LOG_LEVEL_DEBUG, 1024, (TAL_LOG_OUTPUT_CB)tkl_log_output);
    // 打印应用程序信息
    PR_NOTICE("应用程序信息:");
	PR_NOTICE("项目名称:        %s", PROJECT_NAME);
	PR_NOTICE("应用版本:         %s", PROJECT_VERSION);
	PR_NOTICE("编译时间:        %s", __DATE__);
	PR_NOTICE("TuyaOpen版本:    %s", OPEN_VERSION);
	PR_NOTICE("TuyaOpen提交ID:  %s", OPEN_COMMIT);
	PR_NOTICE("平台芯片:       %s", PLATFORM_CHIP);
	PR_NOTICE("平台板型:      %s", PLATFORM_BOARD);
	PR_NOTICE("平台提交ID:  %s", PLATFORM_COMMIT);
    // 初始化键值存储系统
    tal_kv_init(&(tal_kv_cfg_t){
        .seed = "vmlkasdh93dlvlcy", // 加密种子
        .key = "dflfuap134ddlduq", // 加密密钥
    });
    tal_sw_timer_init(); // 初始化软件定时器系统
    tal_workq_init(); // 初始化工作队列系统

// 在非Ubuntu平台上初始化CLI和授权系统
#if !defined(PLATFORM_UBUNTU) || (PLATFORM_UBUNTU == 0)
    tal_cli_init(); // 初始化命令行接口
    tuya_authorize_init(); // 初始化Tuya授权系统
    tuya_app_cli_init(); // 初始化应用程序特定的CLI命令
#endif
    // 启动网络配置重置功能
    reset_netconfig_start();
    // 读取设备授权信息
    if (OPRT_OK != tuya_authorize_read(&license)) {
        PR_WARN("读取设备授权信息失败，使用配置文件中的uuid和authkey");
        license.uuid = TUYA_OPENSDK_UUID;
        license.authkey = TUYA_OPENSDK_AUTHKEY;
        PR_WARN("Replace the TUYA_OPENSDK_UUID and TUYA_OPENSDK_AUTHKEY contents, otherwise the demo cannot work.\n \
                Visit https://platform.tuya.com/purchase/index?type=6 to get the open-sdk uuid and authkey.");
    }
    PR_INFO("xxs uuid %s, authkey %s", license.uuid, license.authkey);
    // // 初始化Tuya设备配置
    rt = tuya_iot_init(&client, &(const tuya_iot_config_t){
                                    .software_ver = PROJECT_VERSION, // 软件版本
                                    .productkey = TUYA_PRODUCT_ID,  // 产品ID
                                    .uuid = license.uuid, // 设备唯一标识符
                                    .authkey = license.authkey,  // 授权密钥
                                    .event_handler = user_event_handler_on, // 事件处理回调函数
                                    .network_check = user_network_check, // 网络检查回调函数
                                });
    // 确保初始化成功，否则触发断言
    assert(rt == OPRT_OK);

// 如果启用了LwIP协议栈，则初始化
#if defined(ENABLE_LIBLWIP) && (ENABLE_LIBLWIP == 1)
    TUYA_LwIP_Init();
#endif
    // 网络模块初始化
    netmgr_type_e type = 0;
#if defined(ENABLE_WIFI) && (ENABLE_WIFI == 1)
    type |= NETCONN_WIFI;   // 启用WiFi网络
#endif
#if defined(ENABLE_WIRED) && (ENABLE_WIRED == 1)
    type |= NETCONN_WIRED;  // 启用有线网络
#endif
#if defined(ENABLE_CELLULAR) && (ENABLE_CELLULAR == 1)
    type |= NETCONN_CELLULAR; // 启用蜂窝网络
#endif
    // 初始化网络管理器 
    netmgr_init(type);
// 如果启用了WiFi，则配置WiFi网络参数
#if defined(ENABLE_WIFI) && (ENABLE_WIFI == 1)
    netmgr_conn_set(NETCONN_WIFI, NETCONN_CMD_NETCFG, &(netcfg_args_t){.type = NETCFG_TUYA_BLE | NETCFG_TUYA_WIFI_AP});
#endif

    PR_DEBUG("tuya_iot_init成功");
    // 启动Tuya IoT任务
    tuya_iot_start(&client);
    // 检查是否需要重置网络配置
    reset_netconfig_check();
    // 主循环，持续处理Tuya IoT消息和保持连接
    for (;;) {
        // 循环接收数据包，并处理客户端保活
        tuya_iot_yield(&client);
    }
}

/**
 * Linux系统的主函数
 * 当在Linux系统上运行时，这是程序的入口点
 * @param argc:命令行参数数量
 * @param argv:命令行参数数组
 * @return void
 */
#if OPERATING_SYSTEM == SYSTEM_LINUX
void main(int argc, char *argv[])
{
    PR_INFO("xxs 系统是Linux系统，开始启动main主函数");
    user_main(); // 调用用户主函数
}
#else

// Tuya应用线程句柄
static THREAD_HANDLE ty_app_thread = NULL;

/**
 * Tuya应用线程函数
 * 在非Linux系统上，创建一个线程来运行用户主函数
 * @param[in] arg:创建任务时的参数（未使用）
 * @return none
 */
static void tuya_app_thread(void *arg)
{
    user_main();
    // 任务完成后删除线程（在实际应用中，user_main通常不会返回）
    tal_thread_delete(ty_app_thread);
    ty_app_thread = NULL;
}

void tuya_app_main(void)
{
    // 配置Tuya应用线程参数
	PR_INFO("xxs 系统不是Linux系统，开始启动main主函数");
	PR_INFO("xxs 开始启动tuya_app_main");
    THREAD_CFG_T thrd_param = {4096, 4, "tuya_app_main"}; // 堆栈大小：4096字节 优先级：中等优先级 线程名称
    // 创建并启动Tuya应用线程
    tal_thread_create_and_start(&ty_app_thread, NULL, NULL, tuya_app_thread, NULL, &thrd_param);
}
#endif
